/** ==================================================================================== **/
/**     					 Definizione dei servizi della mappa.		 				 **/
/** ==================================================================================== **/

/**
 * 
 */
BootApplication.service('olMapService', 
		[ '$translate', 'log', 'MapOptions', 'olAttributeService', 'LoadingAppService', 'DialogService', 'olControlService', 
        function( $translate, log, MapOptions, olAttributeService, LoadingAppService, DialogService, olControlService) {
	
	/** 
	 * Creare una nuova mappa con il costruttore ol.Map.
	 * 
	 * @param {@link MapOptions} options
	 * 
	 * @return {@link ol.Map}
	 */
	this.createMap2D = function(options) {
		try{
			// loading ..
			LoadingAppService.showLoader($translate.instant("Loader.buildMap2D"));
			
			/**
			 * Creo l'oggetto ol.View
			 */
			var olView = olAttributeService.createAttributeOlView(options);
			/**
			 * Configuro i controlli di default (se attivi):
			 *   - ol.control.Zoom 
			 *   - ol.control.Rotate
			 *   - ol.control.Attribution
			 */
			var olControlDefaults = olAttributeService.createAttributeOlControlDefaults(options);
			/** 
			 * Configuro le interazioni di default.
			 */
			var olInteraction = olAttributeService.createAttributeOlInteraction();
			/** 
			 * Ritorna la lista dei layer di base da aggiungere alla mappa.
			 */
			var olBaseLayer = olAttributeService.createAttributeOlBaseLayer(options);
			/** 
			 * Ritorna l'oggetto map.
			 */
			var olMap = olAttributeService.createAttributeOlMap(options, olView, olControlDefaults, olInteraction, olBaseLayer);
			
			/** 
			 * Aggiunge alla mappa la lista dei controlli non di default abilitati, configurati in base alle options fornite. 
			 */
			olAttributeService.createAttributeOlControlAdvanced(olMap, options);

			// spengo il loading
			LoadingAppService.hideLoader();
			
			return olMap;
		}catch(e){
			/**
			 * La procedura di costruzione della mappa 2D è andata in errore, apro una popup per informativa dell'errore.
			 */
			log.error("Error build map 2D " + e, e.stack);
			/**
    		 * spengo il loading
    		 */
			LoadingAppService.hideLoader();
			/**
			 * apri popup errore
			 */
			DialogService.showErrorAlertApplication($translate.instant('Init.build.map2d.error'));

			return false;
		}
	}// close createMap2D
	
	/** 
	 * Creare la mappa 3D con il costruttore olcs.OLCesium.
	 * 
	 * @param {@link ol.Map} olMap
	 * @param {@link MapOptions} options, opzioni mappa
	 * 
	 * @return {@link olcs.OLCesium}
	 */
	this.createMap3D = function(olMap, options) {
		try{
			// loading ..
			LoadingAppService.showLoader($translate.instant("Loader.buildMap3D"));
			
			/** 
			 * Ritorna l'oggetto map.
			 */
			var olMap3D = null;//new olcs.OLCesium({map: olMap});
			/** 
			 * Aggiungo alla mappa il controllo di openlayers che attiva e disattiva il 3D della mappa 
			 */
			olMap.addControl(olControlService.create3DControl(olMap3D, options));
			
			// spengo il loading
			LoadingAppService.hideLoader();
			
			return olMap3D;
		}catch(e){
			/**
			 * La procedura di costruzione della mappa 3D è andata in errore, apro una popup informativa dell'errore.
			 */
			log.error("Error build map 3D " + e, e.stack);
			/**
			 *  spengo il loading
			 */
			LoadingAppService.hideLoader();
			/**
			 * apri popup errore
			 */
			DialogService.showErrorAlertApplication($translate.instant('Init.build.map3d.error'));

			return false;
		}
	}// close createMap3D
	
}]);


/**
 * 
 */
BootApplication.service('olAttributeService', 
		[ '$translate', 'log', 'MapOptions', 'olFormatService', 'olControlService', 'olInteractionService', 'LoadingAppService', 'DialogService', 'olLayerService',
        function($translate, log, MapOptions, olFormatService, olControlService, olInteractionService, LoadingAppService, DialogService, olLayerService) {
	
	/** 
	 * Ritorna l'oggetto map.
	 * 
	 * @param {@link MapOptions} options, opzioni mappa
	 * @param {@link ol.View} olView
	 * @param {@link ol.Collection} olControlDefaults
	 * @param {@link ol.Collection} olInteraction
	 * @param {@link ol.Collection} olBaseLayer
	 * @return {@link ol.Map}
	 */
	this.createAttributeOlMap = function(options, olView, olControlDefaults, olInteraction, olBaseLayer) {
		
		var olMap = new ol.Map({
			/**
			 * Configuro i controlli di default (se attivi):
			 *   - ol.control.Zoom 
			 *   - ol.control.Rotate
			 *   - ol.control.Attribution
			 */
			controls: olControlDefaults,
			/**
			 * The ratio between physical pixels and device-independent pixels (dips) on the device. 
			 * If undefined then it gets set by using window.devicePixelRatio.
			 * {@link number | undefined}
			 */
//			pixelRatio: 
			/**
			 * Interactions that are initially added to the map. If not specified, ol.interaction.defaults() is used.
			 */
			interactions: olInteraction,
			/** 
			 * The element to listen to keyboard events on 
			 * **/
			keyboardEventTarget: options.getTargetMap(),
			/** 
			 * Funzione per la costruzione dei layer di base della mappa. Layers. If this is not defined, a map with no layers will be rendered.
			 ***/
			layers: olBaseLayer,
			/**
			 * When set to true, tiles will be loaded during animations. This may improve the user experience, but can also 
			 * make animations stutter on devices with slow memory. Default is false.
			 */
			loadTilesWhileAnimating: true,
			/**
			 * When set to true, tiles will be loaded while interacting with the map. This may improve the user experience, but can 
			 * also make map panning and zooming choppy on devices with slow memory. Default is false.
			 */
			loadTilesWhileInteracting: true,
			/**
			 * Il logo mappa. Un logo da visualizzare sulla mappa in ogni momento. Se viene fornita una stringa, viene impostato come 
			 * fonte immagine del logo. Se viene fornito un oggetto, la proprietà src dovrebbe essere l'URL di un'immagine e 
			 * la proprietà href dovrebbe essere un URL per la creazione di un collegamento. 
			 * Per disabilitare il logo mappa, impostare l'opzione a false. Per impostazione predefinita, viene mostrato il logo di OpenLayers 3.
			 * {@link boolean | string | olx.LogoOptions | undefined }
			 */
			logo: false,
			/**
			 * Overlays inizialmente aggiunto alla mappa. Per impostazione predefinita, non vengono aggiunti Overlays.
			 */
			overlays: [],
			/**
			 * Renderer. By default, Canvas, DOM and WebGL renderers are tested for support in that order, and the first supported used. 
			 * Specify a ol.RendererType here to use a specific renderer. Note that at present only the Canvas renderer supports vector data.
			 * {@link ol.RendererType | Array.<(ol.RendererType|string)> | string | undefined }
			 */
//			renderer: 
			/** 
			 * The container for the map, either the element itself or the id of the element. 
			 * **/
			target: options.getTargetMap(),
			/**
			 * The map's view. No layer sources will be fetched unless this is specified at construction time or through ol.Map#setView.
			 */
			view: olView
		});
		/**
		 * Aggiungo all'instanza di mappa le options che hanno determinato il build della stessa.
		 */
		olMap.setOptions(options);
		
		return olMap;
	}// close createAttributeOlMap
	
	/** 
	 * Ritorna un oggetto corrispondente all'attributo "olView".
	 * 
	 * Esempio semplificato del modello:
	 * 		view: {
	 * 		    projection: 'EPSG:3857',
	 * 			minZoom: undefined,
	 * 			maxZoom: undefined,
	 * 			rotation: 0,
	 * 			extent: undefined
	 * 		}
	 * 
	 * @param {@link MapOptions} options, opzioni mappa
	 * @return {@link ol.View}
	 */
	this.createAttributeOlView = function(options) {
		
		var view =
			new ol.View({
				/**
				 * The initial center for the view. The coordinate system for the center is specified with the projection option. 
				 * Default is undefined, and layer sources will not be fetched if this is not set.
				 * {@link ol.Coordinate | undefined	}
				 */
				center: olFormatService.olProjTransorm(options.getCenter()[0], options.getCenter()[1], options.getSourceProjection(), options.getDestProjection()),
	            /**
	     		 * Rotation constraint. false means no constraint. true means no constraint, but snap to zero near zero. 
	     		 * A number constrains the rotation to that number of values. For example, 4 will constrain the rotation to 0, 90, 180, and 270 degrees. 
	     		 * The default is true.
	     		 * {@link boolean | number | undefined }
	     		 */
	     		constrainRotation: true,
	     		/**
	     		 * Abilita rotazione(SHIFT+ALT+MOUSE). Il valore predefinito è vero.
	     		 * L'opzione "constrainRotation" non ha effetto se "enableRotation" è falso.
	     		 * {@link boolean | undefined }
	     		 */
	     		enableRotation: options.isEnableRotation(),	
	     		/**
	    		 * La misura che vincola il centro, in altre parole, il centro non può essere impostato fuori misura.
	    		 * {@link ol.Extent | undefined }
	    		 */
	//    		extent: options.getEextent(),  // TODO
	     		/**
	     		 * The maximum resolution used to determine the resolution constraint. It is used together with minResolution (or maxZoom) and zoomFactor. 
	     		 * If unspecified it is calculated in such a way that the projection's validity extent fits in a 256x256 px tile. 
	     		 * If the projection is Spherical Mercator (the default) then maxResolution defaults to 40075016.68557849 / 256 = 156543.03392804097.
	     		 * {@link number | undefined }
	     		 */
	//     		maxResolution: undefined,
	     		/**
	     		 * The minimum resolution used to determine the resolution constraint. It is used together with maxResolution (or minZoom) and zoomFactor. 
	     		 * If unspecified it is calculated assuming 29 zoom levels (with a factor of 2). If the projection is Spherical Mercator (the default) 
	     		 * then minResolution defaults to 40075016.68557849 / 256 / Math.pow(2, 28) = 0.0005831682455839253.
	     		 * {@link number | undefined }
	     		 */
	//     		minResolution: undefined,
	     		/**
	    		 * Livello minimo di zoom.
	    		 * {@link number | undefined }
	    		 */
	     		minZoom: options.getMinZoom(),
	     		/**
	    		 * Livello massimo di zoom.
	    		 * {@link number | undefined }
	    		 */
	            maxZoom: options.getMaxZoom(),
	            /**
	    		 * The projection.
	    		 * {@link ol.proj.ProjectionLike }
	    		 */
	    		projection: options.getDestProjection(),
	    		/**
	    		 * The initial resolution for the view. The units are projection units per pixel (e.g. meters per pixel). An alternative to setting 
	    		 * this is to set zoom. Default is undefined, and layer sources will not be fetched if neither this nor zoom are defined.
	    		 * {@link number | undefined }
	    		 */
	//    		resolution: undefined,
	    		/**
	    		 * Risoluzioni per determinare il vincolo risoluzione. Se impostato verrano ignorate le opzioni: maxResolution, minResolution, minZoom, 
	    		 * maxZoom, and zoomFactor.
	    		 * {@link Array.<number> | undefined }
	    		 */
	//    		resolutions: [],
	    		/**
	    		 * The initial rotation for the view in radians (positive rotation clockwise). Default is 0.
	    		 * {@link number | undefined }
	    		 */
	    		rotation: 0,	
	    		/**
	    		 * Utilizzato solo se la risoluzione non è definito. Livello di zoom utilizzato per calcolare la risoluzione iniziale per la 
	    		 * visualizzazione. La risoluzione iniziale è determinato con il metodo ol.View # constrainResolution.
	    		 * {@link number | undefined }
	    		 */
	    		zoom: options.getZoom(),
	    		/**
	    		 * The zoom factor used to determine the resolution constraint. Default is 2.
	    		 * {@link number | undefined }
	    		 */
	    		zoomFactor: 2
			});
		
		return view;
	}// close createAttributeOlView
	
	/** 
	 * Ritorna la lista dei controlli di default da aggiungere la mappa, configurati in base alle options fornite. 
	 * Un controllo di default, a partire dalle options, può essere reso attivo o no e per ciascuno possiamo
	 * personalizzare le proprietà che lo definiscono.
	 * 
	 * I controlli di deafult sono:
	 * 
	 *   - ol.control.Rotate
	 *   - ol.control.Attribution
	 *   
	 * @param {@link MapOptions} options, opzioni mappa
	 * @return {@link ol.Control}
	 */
	this.createAttributeOlControlDefaults = function(options) {
		
		return olControlService.createOlControlDefaults(options);
	}// close createAttributeOlControlDefaults
	
	/** 
	 * Aggiunge alla mappa la lista dei controlli non di defautl abilitati, configurati in base alle options fornite. 
	 * Un controllo è un widget visibile con un elemento DOM in una posizione fissa sullo schermo. 
	 * Essi possono coinvolgere l'input dell'utente (pulsanti), o essere solo informativo; la posizione è determinato 
	 * utilizzando i CSS.
	 * 
	 * @param {@link ol.Map} mappa 2D
	 * @param {@link MapOptions} options, opzioni mappa
	 */
	this.createAttributeOlControlAdvanced = function(olMap, options) {
		try{
			// loading ..
			LoadingAppService.showLoader($translate.instant("Loader.buildControlMap"));
			
			/**
			 * Verifico se e quali controlli non di default aggiungere alla mappa
			 */
			if(options.isEnableMousePosition()) olMap.addControl(olControlService.createMousePositionControl(options));
			if(options.isEnableZoomToExtent()) 	olMap.addControl(olControlService.createZoomToExtentControl(options));
			if(options.isEnableOverviewMap()) 	olMap.addControl(olControlService.createOverviewMapControl(options));
			if(options.isEnableScaleLine()) 	olMap.addControl(olControlService.createScaleLineControl());
			if(options.isEnableZoomSlider()) 	olMap.addControl(olControlService.createZoomSliderControl());
			if(options.isEnableLayerSwitcher())	olMap.addControl(olControlService.createLayerSwitcherControl(options));
			if(options.isEnableFullscreen()) 	olMap.addControl(olControlService.createFullscreenControl(options));
			
			// spengo il loading
			LoadingAppService.hideLoader();
				
			return true;
		}catch(e){
			/**
			 * La procedura di costruzione dei controlli della mappa è andata in errore, apro una popup informativa dell'errore.
			 */
			log.error("Error build advanced control " + e, e.stack);
			/**
			 * spengo il loading
			 */
			LoadingAppService.hideLoader();
			/**
			 * apri popup errore
			 */
			DialogService.showErrorAlertApplication($translate.instant('Init.build.controlmap.error'));
				
			return false;
		}
	}// close createAttributeOlControlAdvanced
	
	/** 
	 * Ritorna la lista delle interaction da aggiungere alla mappa.
	 * Per interaction s'intendono le azioni degli utenti che modificano lo stato della mappa. Alcune interaction sono simili ai controlli,
	 * ma non sono associati a un elemento DOM. 
	 * Ad esempio, ol.interaction.KeyboardZoom è funzionalmente lo stesso di ol.control.Zoom, ma è innescata da un'evento di tastiera e non da un 
	 * pulsante.
	 * 
	 * @return {@link ol.Collection}
	 */
	this.createAttributeOlInteraction = function() {
		try{
			var interactions = new ol.Collection();
			
			// loading ..
			LoadingAppService.showLoader($translate.instant("Loader.buildInteractionMap"));
			
			/** Consente all'utente di ruotare la mappa (Alt-Shift-drag) **/
			interactions.push(olInteractionService.altShiftDragRotate());
			/** Consente all'utente di ingrandire con un doppio click sulla mappa. **/
//			interactions.push(olInteractionService.doubleClickZoom());
			/** Consente all'utente di spostare la mappa con le frecce della tastiera. **/
			interactions.push(olInteractionService.keyboardPan());
			/** Consente all'utente di ingrandire la mappa utilizzando i tasti della tastiera + e -. **/
			interactions.push(olInteractionService.keyboardZoom());
			/** Consente all'utente di ingrandire la mappa facendo scorrere la rotellina del mouse. **/
			interactions.push(olInteractionService.mouseWheelZoom());
			/** Consente all'utente di ingrandire la mappa cliccando e trascinando sulla mappa (shift+box) **/
			interactions.push(olInteractionService.shiftDragZoom());
			/** Consente all'utente di scorrere la mappa trascinando la mappa. **/
			interactions.push(olInteractionService.dragPan());
			/** Consente all'utente di ruotare la mappa ruotando con due dita su un touch screen. **/
			interactions.push(olInteractionService.pinchRotate());
			/** Consente all'utente di ingrandire la mappa pizzicando con due dita su un touch screen. **/
			interactions.push(olInteractionService.pinchZoom());
			
			// spengo il loading
			LoadingAppService.hideLoader();
			
			return interactions;
		}catch(e){
			/**
			 * La procedura di costruzione delle interaction della mappa è andata in errore, apro una popup informativa dell'errore.
			 */
			log.error("Error build interactions " + e, e.stack);
			/**
			 *  spengo il loading
			 */
			LoadingAppService.hideLoader();
			/**
			 * apri popup errore
			 */
			DialogService.showErrorAlertApplication($translate.instant('Init.build.interactionmap.error'));
			
			return [];
		}
	}// close createAttributeOlInteraction
	
	/** 
	 * Ritorna la lista dei layer di base da aggiungere alla mappa.
	 *   
	 * @param {@link MapOptions} options, opzioni mappa
	 * @return {@link ol.Collection}
	 */
	this.createAttributeOlBaseLayer = function(options) {
		try{
			var layers = new ol.Collection();
			
			// loading ..
			LoadingAppService.showLoader($translate.instant("Loader.buildBaseLayer"));
			
			if(options.getBasicLayers().isBingRoad()) 			  layers.push(olLayerService.createLayerBingRoad());
			if(options.getBasicLayers().isBingAerial()) 		  layers.push(olLayerService.createLayerBingAerial());
			if(options.getBasicLayers().isBingAerialWithLabels()) layers.push(olLayerService.createLayerBingAerialWithLabels());
			if(options.getBasicLayers().isGoogleSat()) 	   		  layers.push(olLayerService.createLayerGoogleSat());
			if(options.getBasicLayers().isGoogleHybrid())  		  layers.push(olLayerService.createLayerGoogleHybrid());
			if(options.getBasicLayers().isGoogleTerrain())  	  layers.push(olLayerService.createLayerGoogleTerrain());
			if(options.getBasicLayers().isGoogleRoadmap())  	  layers.push(olLayerService.createLayerGoogleRoadMap());
			if(options.getBasicLayers().isOpenStreetMap()) 		  layers.push(olLayerService.createLayerOpenStreetMap());
			if(options.getBasicLayers().isOsmLigth()) 		 	  layers.push(olLayerService.createLayerOsmLigth());
			if(options.getBasicLayers().isBlank())  		  	  layers.push(olLayerService.createLayerVuoto());
			
			if(layers.getLength() == 0) log.warn("ATTENZIONE!!! Non sono stati definiti layer di base.");
			
			// spengo il loading
			LoadingAppService.hideLoader();
			
			return layers;
		}catch(e){
			/**
			 * La procedura di costruzione dei layer di base della mappa è andata in errore, apro una popup informativa dell'errore.
			 */
			log.error("Error build layers " + e, e.stack);
			/**
			 *  spengo il loading
			 */
			LoadingAppService.hideLoader();
			/**
			 * apri popup errore
			 */
			DialogService.showErrorAlertApplication($translate.instant('Init.build.layers.error'));
			
			return [];
		}
	}// close createAttributeOlBaseLayer
}]);
